package com.piggy.logi.sys.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.net.MediaType;
import com.piggy.common.core.constant.SecurityConstants;
import com.piggy.common.core.domain.R;
import com.piggy.common.core.exception.GlobalException;
import com.piggy.common.core.utils.PageUtils;
import com.piggy.common.core.utils.sql.SqlUtil;
import com.piggy.common.core.web.page.PagePlus;
import com.piggy.common.core.web.page.TableDataInfo;
import com.piggy.common.satoken.utils.SecurityUtils;
import com.piggy.logi.sys.bo.LogiCustomerCheckBillBo;
import com.piggy.logi.sys.bo.LogiCustomerCheckBillQueryBo;
import com.piggy.logi.sys.domain.*;
import com.piggy.logi.sys.enums.CheckBillStatusEnum;
import com.piggy.logi.sys.enums.SettleStatusEnum;
import com.piggy.logi.sys.enums.SettleTypeEnum;
import com.piggy.logi.sys.enums.WaybillStatusEnum;
import com.piggy.logi.sys.mapper.LogiCustomerCheckBillMapper;
import com.piggy.logi.sys.service.*;
import com.piggy.logi.sys.vo.*;
import com.piggy.sys.api.RemoteOssService;
import com.piggy.sys.api.RemoteSmsService;
import com.piggy.sys.api.bo.OssFileUploadBo;
import com.piggy.sys.api.bo.SysEmailBo;
import com.piggy.sys.api.vo.SysOssVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.font.PDType0Font;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Base64Utils;

import javax.annotation.Resource;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 客户对账Service业务层处理
 *
 * @author fengxy
 * @date 2023/10/17 21:54
 **/
@Slf4j
@Service
public class LogiCustomerCheckBillServiceImpl extends ServiceImpl<LogiCustomerCheckBillMapper, LogiCustomerCheckBill> implements ILogiCustomerCheckBillService {

    @Resource
    private ILogiWaybillService logiWaybillService;

    @Resource
    private ILogiCustomerService logiCustomerService;

    @Resource
    private ILogiWaybillReceivableService logiWaybillReceivableService;

    @Resource
    private ILogiCustomerCheckBillRecordService logiCustomerCheckBillRecordService;


    @Resource
    private RemoteOssService remoteOssService;

    @Resource
    private RemoteSmsService remoteSmsService;

    @Resource
    private ILogiSysBaseConfigService logiSysBaseConfigService;

    @Override
    public TableDataInfo<LogiCustomerCheckBillVo> queryPageList(LogiCustomerCheckBillQueryBo bo) {
        PagePlus<LogiCustomerCheckBill, LogiCustomerCheckBillVo> pageVo = pageVo(PageUtils.buildPagePlus(bo, true), buildQueryWrapper(bo), LogiCustomerCheckBillVo.class);
        pageVo.getRecordsVo().forEach(a -> fillData(a, bo.getDeptId(), bo.getCreateStartDate(), bo.getCreateEndDate()));
        return PageUtils.buildDataInfo(pageVo);
    }

    @Override
    public LogiCustomerCheckBillVo queryInfo(LogiCustomerCheckBillBo bo) {
        LogiCustomerCheckBillVo checkBillVo = getVoOne(SqlUtil.dataPermissionFilter(LogiCustomerCheckBill.class, bo.getDeptId())
                .eq(LogiCustomerCheckBill::getCustomerId, bo.getCustomerId()), LogiCustomerCheckBillVo.class);
        if (ObjectUtil.isNull(checkBillVo)) {
            throw new GlobalException("客户对账信息不存在");
        }
        // 获取开始时间和结束时间
        DateTime startDate = DateUtil.beginOfMonth(DateUtil.parse(bo.getYear() + bo.getMonth() + "01")),
                endDate = DateUtil.endOfMonth(startDate);
        fillData(checkBillVo, bo.getDeptId(), startDate.toString(), endDate.toString());
        return checkBillVo;
    }

    @Override
    public LogiCustomerCheckBillRecordVo recordInfo(LogiCustomerCheckBillBo bo) {
        // 查询客户对账记录
        LogiCustomerCheckBillRecord checkBillRecord = getCheckBillRecord(bo.getDeptId(), bo.getCustomerId(), bo.getYear(), bo.getMonth());
        LogiCustomerCheckBillRecordVo checkBillRecordVo = BeanUtil.toBean(checkBillRecord, LogiCustomerCheckBillRecordVo.class);
        // 设置应收总额、未结算金额和已结算金额
        List<LogiWaybillReceivable> receivableList = getReceivableList(bo.getDeptId(), checkBillRecordVo.getCustomerId(), bo.getYear(), bo.getMonth());
        if (CollUtil.isNotEmpty(receivableList)) {
            checkBillRecordVo.setPayableAmount(receivableList.stream().map(LogiWaybillReceivable::getReceivableAmount).reduce(BigDecimal.ZERO, BigDecimal::add));
            checkBillRecordVo.setSettledAmount(receivableList.stream().map(LogiWaybillReceivable::getRealAmount).reduce(BigDecimal.ZERO, BigDecimal::add));
            checkBillRecordVo.setNoSettleAmount(receivableList.stream().map(LogiWaybillReceivable::getReceivableBalance).reduce(BigDecimal.ZERO, BigDecimal::add));
        } else {
            checkBillRecordVo.setPayableAmount(BigDecimal.ZERO);
            checkBillRecordVo.setSettledAmount(BigDecimal.ZERO);
            checkBillRecordVo.setNoSettleAmount(BigDecimal.ZERO);
        }
        return checkBillRecordVo;
    }

    @Override
    public TableDataInfo<LogiCustomerWayBillVo> wayBillList(LogiCustomerCheckBillBo bo) {
        Date startTime = DateUtil.beginOfMonth(DateUtil.parse(bo.getYear() + bo.getMonth() + "01")), endTime = DateUtil.endOfMonth(startTime);
        LambdaQueryWrapper<LogiWaybillReceivable> wrapper = SqlUtil.dataPermissionFilter(LogiWaybillReceivable.class, bo.getDeptId())
                .eq(LogiWaybillReceivable::getPayerId, bo.getCustomerId())
                .exists("select 1 from logi_waybill where logi_waybill.waybill_id = logi_waybill_receivable.waybill_id and logi_waybill.status = " + WaybillStatusEnum.COMPLETED.getCode())
                .between(LogiWaybillReceivable::getCreateTime, startTime, endTime);
        PagePlus<LogiWaybillReceivable, LogiCustomerWayBillVo> pageVo = logiWaybillReceivableService.pageVo(PageUtils.buildPagePlus(bo, true), wrapper, LogiCustomerWayBillVo.class);
        fillData(pageVo.getRecordsVo());
        return PageUtils.buildDataInfo(pageVo);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void confirmCheck(LogiCustomerCheckBillBo bo) {
        // 参数校验
        if (CollUtil.isEmpty(bo.getReceivableRecordIds())) {
            throw new GlobalException("请选择要对账的运单");
        }
        // 通过应收记录获取对账金额
        List<LogiWaybillReceivable> receivableList = getReceivableList(bo.getDeptId(), bo.getCustomerId(), bo.getYear(), bo.getMonth());
        BigDecimal payableAmount = BigDecimal.ZERO;
        List<LogiWaybillReceivable> updateReceivableList = CollUtil.newArrayList();
        LogiSysBaseConfig sysBaseConfig = logiSysBaseConfigService.getBaseConfig(bo.getDeptId());
        for (LogiWaybillReceivable receivable : receivableList) {
            if (bo.getReceivableRecordIds().contains(receivable.getId())) {
                // 判断运单结清状态，只有已结清的才可以进行对账
                if (sysBaseConfig.getSettledCheckBill() == 1 &&
                        receivable.getStatus() == SettleStatusEnum.NOT_SETTLE.getCode()) {
                    throw new GlobalException("运单【" + receivable.getOutCode() + "】未完成结清");
                }
                receivable.setCheckBillStatus(CheckBillStatusEnum.CHECKED.getCode());
                payableAmount = payableAmount.add(receivable.getReceivableAmount());
                updateReceivableList.add(receivable);
            }
        }
        // 查询该月份的对账信息
        LogiCustomerCheckBillRecord checkBillRecord = getCheckBillRecord(bo.getDeptId(), bo.getCustomerId(), bo.getYear(), bo.getMonth());
        // 判断状态是否是待对账
        if (checkBillRecord.getStatus() != CheckBillStatusEnum.NOT_CHECK.getCode()) {
            throw new GlobalException("当月已对账");
        }
        // 修改该月份的对账状态为：已对账
        checkBillRecord.setStatus(CheckBillStatusEnum.CHECKED.getCode());
        checkBillRecord.setCheckUser(SecurityUtils.getUsername());
        checkBillRecord.setCheckTime(DateUtil.date());
        checkBillRecord.setElectronBill(String.valueOf(genElectronBill(checkBillRecord)));
        boolean success = logiCustomerCheckBillRecordService.saveOrUpdate(checkBillRecord);
        if (!success) {
            throw new GlobalException("修改该月份的对账状态为已对账失败");
        }
        // 修改客户对账列表中的对账人信息
        LogiCustomerCheckBill checkBill = getOne(SqlUtil.dataPermissionFilter(LogiCustomerCheckBill.class, bo.getDeptId())
                .eq(LogiCustomerCheckBill::getCustomerId, bo.getCustomerId()));
        checkBill.setCheckUser(SecurityUtils.getUsername());
        checkBill.setCheckTime(DateUtil.date());
        success = saveOrUpdate(checkBill);
        if (!success) {
            throw new GlobalException("修改客户对账列表中的对账人信息失败");
        }
        // 修改应收记录的对账状态
        success = logiWaybillReceivableService.updateBatchById(updateReceivableList);
        if (!success) {
            throw new GlobalException("确认对账失败，请联系管理员");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancelCheck(LogiCustomerCheckBillBo bo) {
        // 查询该月份的对账信息
        LogiCustomerCheckBillRecord checkBillRecord = getCheckBillRecord(bo.getDeptId(), bo.getCustomerId(), bo.getYear(), bo.getMonth());
        // 判断状态是否是已对账
        if (checkBillRecord.getStatus() != CheckBillStatusEnum.CHECKED.getCode()) {
            throw new GlobalException("当月未对账");
        }
        // 通过应收记录获取对账金额
        List<LogiWaybillReceivable> receivableList = getReceivableList(bo.getDeptId(), bo.getCustomerId(), bo.getYear(), bo.getMonth());
        BigDecimal payableAmount = BigDecimal.ZERO;
        List<LogiWaybillReceivable> updateReceivableList = CollUtil.newArrayList();
        for (LogiWaybillReceivable receivable : receivableList) {
            if (receivable.getCheckBillStatus() == CheckBillStatusEnum.CHECKED.getCode()) {
                payableAmount = payableAmount.add(receivable.getReceivableAmount());
                receivable.setCheckBillStatus(CheckBillStatusEnum.NOT_CHECK.getCode());
                updateReceivableList.add(receivable);
            }
        }
        // 修改该月份的对账状态为：未对账
        checkBillRecord.setStatus(CheckBillStatusEnum.NOT_CHECK.getCode());
        checkBillRecord.setElectronBill(null);
        checkBillRecord.setCheckUser(null);
        checkBillRecord.setCheckTime(null);
        boolean success = logiCustomerCheckBillRecordService.saveOrUpdate(checkBillRecord);
        if (!success) {
            throw new GlobalException("修改该月份的对账状态为未对账失败");
        }
        // 清空客户对账列表中的对账人信息
        LogiCustomerCheckBill checkBill = getOne(SqlUtil.dataPermissionFilter(LogiCustomerCheckBill.class, bo.getDeptId())
                .eq(LogiCustomerCheckBill::getCustomerId, bo.getCustomerId()));
        checkBill.setCheckUser(null);
        checkBill.setCheckTime(null);
        success = saveOrUpdate(checkBill);
        if (!success) {
            throw new GlobalException("清空客户对账列表中的对账人信息失败");
        }
        // 修改应收记录的对账状态
        success = logiWaybillReceivableService.updateBatchById(updateReceivableList);
        if (!success) {
            throw new GlobalException("取消对账失败，请联系管理员");
        }
    }

    @Override
    public void sendEmail(LogiCustomerCheckBillBo bo) {
        // 参数校验
        if (StrUtil.isBlank(bo.getEmail())) {
            throw new GlobalException("请填写电子邮箱地址");
        }
        // 查询该月份的对账信息
        LogiCustomerCheckBillRecord checkBillRecord = getCheckBillRecord(bo.getDeptId(), bo.getCustomerId(), bo.getYear(), bo.getMonth());
        // 判断状态是否是已对账
        if (checkBillRecord.getStatus() != CheckBillStatusEnum.CHECKED.getCode()) {
            throw new GlobalException("当月未对账");
        }
        SysEmailBo sysEmailBo = new SysEmailBo();
        sysEmailBo.setContent("{}年{}月对账单，对账人：{}，对账时间：{}");
        sysEmailBo.setSubject(checkBillRecord.getYear() + "年" + checkBillRecord.getMonth() + "月对账单");
        sysEmailBo.setParams(CollUtil.newArrayList(checkBillRecord.getYear(), checkBillRecord.getMonth(), checkBillRecord.getCheckUser(), DateUtil.formatDate(checkBillRecord.getCheckTime())));
        sysEmailBo.setReceiver(bo.getEmail());
        try {
            R<SysOssVo> ossVo = remoteOssService.getSysOssById(checkBillRecord.getElectronBill(), SecurityConstants.INNER);
            SysOssVo vo = ossVo.getData();
            File file = HttpUtil.downloadFileFromUrl(vo.getUrl(), FileUtil.createTempFile("checkBill", "." + vo.getFileType(), true));
            sysEmailBo.setFiles(new File[]{file});
        } catch (Exception e) {
            throw new GlobalException("发票附件添加失败");
        }
        remoteSmsService.sendMail(sysEmailBo, SecurityConstants.INNER);
    }

    /**
     * 组装查询条件
     **/
    private Wrapper<LogiCustomerCheckBill> buildQueryWrapper(LogiCustomerCheckBillQueryBo bo) {
        LambdaQueryWrapper<LogiCustomerCheckBill> lqw = Wrappers.lambdaQuery();
        // 客户名称筛选
        if (StrUtil.isNotBlank(bo.getCustomerName())) {
            String existsSql = "select 1 from logi_customer where logi_customer.customer_id = logi_customer_check_bill.customer_id" +
                    " and logi_customer.customer_name like '%" + bo.getCustomerName() + "%'";
            lqw.exists(existsSql);
        }
        SqlUtil.dataPermissionFilter(lqw, bo.getDeptId());
        return lqw;
    }

    /**
     * 补充字段数据
     **/
    private void fillData(LogiCustomerCheckBillVo checkBillVo, Long deptId, String startDate, String endDate) {
        // 设置客户名称
        LogiCustomer customer = logiCustomerService.getOne(Wrappers.lambdaQuery(LogiCustomer.class)
                .eq(LogiCustomer::getCustomerId, checkBillVo.getCustomerId()));
        checkBillVo.setCustomerName(customer.getCustomerName());
        // 设置对账合计、已对账合计、未对账合计、未结算金额、已结算金额
        BigDecimal checkedAmount = BigDecimal.ZERO, noCheckAmount = BigDecimal.ZERO,
                settledAmount = BigDecimal.ZERO, noSettleAmount = BigDecimal.ZERO;
        // 查询关联运单
        List<LogiWaybill> completedWaybills = logiWaybillService.getCompletedWaybills(deptId, startDate, endDate);
        if (CollUtil.isNotEmpty(completedWaybills)) {
            // 运单id集合
            List<Long> waybillIds = completedWaybills.stream().map(LogiWaybill::getWaybillId).collect(Collectors.toList());
            // 查询符合条件的代收
            List<LogiWaybillReceivable> receivableList = logiWaybillReceivableService.list(Wrappers.lambdaQuery(LogiWaybillReceivable.class).in(LogiWaybillReceivable::getWaybillId, waybillIds).eq(LogiWaybillReceivable::getPayerId, checkBillVo.getCustomerId()));
            // 设置关联运单数量
            checkBillVo.setWaybillNum(receivableList.size());
            for (LogiWaybillReceivable receivable : receivableList) {
                if (receivable.getCheckBillStatus() == CheckBillStatusEnum.CHECKED.getCode()) {
                    checkedAmount = checkedAmount.add(receivable.getReceivableAmount());
                } else {
                    noCheckAmount = noCheckAmount.add(receivable.getReceivableAmount());
                }
                settledAmount = settledAmount.add(receivable.getRealAmount());
                noSettleAmount = noSettleAmount.add(receivable.getReceivableBalance());
            }
        }
        checkBillVo.setTotalAmount(settledAmount.add(noSettleAmount));
        checkBillVo.setCheckedAmount(checkedAmount);
        checkBillVo.setNoCheckAmount(noCheckAmount);
        checkBillVo.setSettledAmount(settledAmount);
        checkBillVo.setNoSettleAmount(noSettleAmount);
    }


    /**
     * 补充字段数据
     **/
    private void fillData(List<LogiCustomerWayBillVo> wayBillVoList) {
        List<LogiWaybillExtVo> waybillExtVoList = wayBillVoList.stream().map(a -> logiWaybillService.getVoById(a.getWaybillId(), LogiWaybillExtVo.class)).collect(Collectors.toList());
        logiWaybillService.fillDate(waybillExtVoList);
        Map<Long, LogiWaybillExtVo> waybillMap = waybillExtVoList.stream().collect(Collectors.toMap(LogiWaybillExtVo::getWaybillId, Function.identity()));
        wayBillVoList.forEach(a -> {
            // 获取运单详情
            LogiWaybillExtVo waybillExtVo = waybillMap.get(a.getWaybillId());
            // 设置运单状态
            a.setWaybillStatus(waybillExtVo.getStatus());
            // 设置货物名称
            a.setGoodsName(waybillExtVo.getGoodSourceList().stream().map(LogiWaybillGoodSourceVo::getGoodsName).collect(Collectors.joining(StrUtil.COMMA)));
            // 设置发货地址
            a.setShipDistrictName(waybillExtVo.getShipDistrictName());
            // 设置收货地址
            a.setDeliveryDistrictName(waybillExtVo.getDeliveryDistrictName());
            // 设置发货方名称
            a.setShipName(waybillExtVo.getShipName());
            // 设置收货方名称
            a.setDeliveryName(waybillExtVo.getDeliveryName());
            // 设置出发网点名称
            a.setShipNetName(waybillExtVo.getShipNetName());
            // 设置收发网点名称
            a.setDeliveryNetName(waybillExtVo.getDeliveryNetName());
            // 设置开票状态
            a.setInvoiceStatus(waybillExtVo.getInvoiceStatus());
            // 设置创建时间
            a.setCreateTime(waybillExtVo.getCreateTime());
        });
    }

    /**
     * 获取客户应收记录
     **/
    private List<LogiWaybillReceivable> getReceivableList(Long deptId, Long customerId, String year, String month) {
        // 获取开始时间和结束时间
        DateTime startDate = null, endDate = null;
        if (StrUtil.isAllNotBlank(year, month)) {
            startDate = DateUtil.beginOfMonth(DateUtil.parse(year + month + "01"));
            endDate = DateUtil.endOfMonth(startDate);
        }
        // 查询关联运单
        List<LogiWaybill> completedWaybills = logiWaybillService.getCompletedWaybills(deptId, ObjectUtil.isNull(startDate) ? "" : startDate.toString(), ObjectUtil.isNull(endDate) ? "" : endDate.toString());
        if (CollUtil.isNotEmpty(completedWaybills)) {
            // 运单id集合
            List<Long> waybillIds = completedWaybills.stream().map(LogiWaybill::getWaybillId).collect(Collectors.toList());
            // 返回符合条件的代收
            return logiWaybillReceivableService.list(Wrappers.lambdaQuery(LogiWaybillReceivable.class).in(LogiWaybillReceivable::getWaybillId, waybillIds).eq(LogiWaybillReceivable::getPayerId, customerId));
        } else {
            return CollUtil.newArrayList();
        }
    }

    /**
     * 根据年月获取客户对账记录
     **/
    private LogiCustomerCheckBillRecord getCheckBillRecord(Long deptId, Long customerId, String year, String month) {
        LogiCustomerCheckBillRecord checkBillRecord = logiCustomerCheckBillRecordService.getOne(
                SqlUtil.dataPermissionFilter(LogiCustomerCheckBillRecord.class, deptId)
                .eq(LogiCustomerCheckBillRecord::getCustomerId, customerId)
                .eq(LogiCustomerCheckBillRecord::getYear, year)
                .eq(LogiCustomerCheckBillRecord::getMonth, month));
        if (ObjectUtil.isNull(checkBillRecord)) {
            checkBillRecord = new LogiCustomerCheckBillRecord();
            checkBillRecord.setDeptId(deptId);
            checkBillRecord.setCustomerId(customerId);
            checkBillRecord.setStatus(CheckBillStatusEnum.NOT_CHECK.getCode());
            checkBillRecord.setYear(year);
            checkBillRecord.setMonth(month);
        }
        return checkBillRecord;
    }

    /**
     * 生成电子对账单
     **/
    private Long genElectronBill(LogiCustomerCheckBillRecord checkBillRecord) {
        // 获取对账应收运单
        List<LogiWaybillReceivable> receivableList = getReceivableList(checkBillRecord.getDeptId(), checkBillRecord.getCustomerId(), checkBillRecord.getYear(), checkBillRecord.getMonth());
        // 设置应付金额、未结算金额和已结算金额
        BigDecimal payableAmount = receivableList.stream().map(LogiWaybillReceivable::getReceivableAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal settledAmount = receivableList.stream().map(LogiWaybillReceivable::getRealAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal receivableBalance = receivableList.stream().map(LogiWaybillReceivable::getReceivableBalance).reduce(BigDecimal.ZERO, BigDecimal::add);
        // 获取标题
        String title = checkBillRecord.getYear() + "年" + checkBillRecord.getMonth() + "月应收总额：" + payableAmount + "，已结算金额：" + settledAmount + "，未结算金额：" + receivableBalance + "。对账人：" + checkBillRecord.getCheckUser() + "，对账时间：" + DateUtil.formatDate(checkBillRecord.getCheckTime());
        // 获取导出内容
        List<List<String>> content = CollUtil.newArrayList();
        content.add(CollUtil.newArrayList("运单编号", "货物名称", "发货地址", "收货地址", "发货方", "收货方", "出发网点", "运单状态", "创建时间", "结算方式", "应收总额(元)", "实收金额(元)", "应收余额(元)", "结清状态"));
        List<LogiCustomerWayBillVo> wayBillList = receivableList.stream().map(any -> BeanUtil.toBean(any, LogiCustomerWayBillVo.class)).collect(Collectors.toList());
        fillData(wayBillList);
        wayBillList.forEach(a -> content.add(CollUtil.newArrayList(
                a.getOutCode(), a.getGoodsName(), a.getShipDistrictName(), a.getDeliveryDistrictName(),
                a.getShipName(), a.getDeliveryName(), a.getShipNetName(), WaybillStatusEnum.get(a.getWaybillStatus()).getInfo(),
                DateUtil.formatDate(a.getCreateTime()), SettleTypeEnum.get(a.getSettlementMode()).getInfo(), a.getReceivableAmount().toString(),
                a.getRealAmount().toString(), a.getReceivableBalance().toString(), SettleStatusEnum.get(a.getStatus()).getInfo()
        )));
        byte[] pdfBytes = drawTable(title, content);
        if (ObjectUtil.isNull(pdfBytes)) {
            log.error("生成对账pdf文件失败");
            return null;
        }
        // 上传文件
        OssFileUploadBo bo = new OssFileUploadBo();
        bo.setFileName(IdWorker.get32UUID() + StrUtil.DOT + MediaType.PDF.subtype());
        bo.setContent(Base64Utils.encodeToString(pdfBytes));
        R<SysOssVo> vo = remoteOssService.uploadToCloud(bo, SecurityConstants.INNER);
        if (ObjectUtil.isNull(vo) || ObjectUtil.isNull(vo.getData())) {
            log.error("上传对账pdf文件失败");
            return null;
        } else {
            return vo.getData().getId();
        }
    }

    /**
     * pdf写入内容
     **/
    public static byte[] drawTable(String title, List<List<String>> content) {
        try {
            // 文件输出流
            ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
            // 创建PDF文档
            PDDocument document = new PDDocument();
            PDPage page = new PDPage(new PDRectangle(1500F, 1000F));
            document.addPage(page);
            PDType0Font font = PDType0Font.load(document, ResourceUtil.getStream("static/simhei.ttf"));
            PDPageContentStream contentStream = new PDPageContentStream(document, page);
            contentStream.setFont(font, 12);
            // 样式
            final float y = page.getMediaBox().getWidth() - 550;
            final float margin = 50;
            final int rows = content.size();
            final int cols = content.get(0).size();
//        final float rowHeight = 20f;
            List<Double> rowHeightList = CollUtil.newArrayList();
            for (List<String> row : content) {
                int textLength = 0;
                for (String col : row) {
                    int length = col.length();
                    if (length > textLength) {
                        textLength = length;
                    }
                }
                rowHeightList.add(Math.ceil(textLength / 8.0) * 20);
            }
            final float tableWidth = page.getMediaBox().getWidth() - (2 * margin);
//        final float tableHeight = rowHeight * rows;
            float tableHeight = 0;
            final float colWidth = tableWidth / (float) cols;
            final float cellMargin = 5f;
            // 标题
            contentStream.beginText();
            contentStream.moveTextPositionByAmount(margin, page.getMediaBox().getWidth() - 530);
            contentStream.drawString(title);
            contentStream.endText();
            //draw the rows
            float nexty = y;
            for (int i = 0; i <= rows; i++) {
                contentStream.drawLine(margin, nexty, margin + tableWidth, nexty);
                if (i != rows) {
                    Double rowHeight = rowHeightList.get(i);
                    tableHeight += rowHeight;
                    nexty -= rowHeight;
                }
            }
            //draw the columns
            float nextx = margin;
            for (int i = 0; i <= cols; i++) {
                contentStream.drawLine(nextx, y, nextx, y - tableHeight);
                nextx += colWidth;
            }
            //now add the text
            float textx = margin + cellMargin;
            float texty = y - 15;
            for (int i = 0; i < content.size(); i++) {
                for (String text : content.get(i)) {
                    // 判断是否有中文，如果有则只可以写7个字符
                    int maxCharNum = Validator.isChinese(text) ? 8 : 16;
                    if (text.length() > maxCharNum) {
                        StringBuilder sb = new StringBuilder(text);
                        float tempy = texty;
                        for (int z = 0; z < Math.ceil((double) sb.length() / maxCharNum); z++) {
                            int charNum = maxCharNum - 1;
                            int startIndex = charNum * z, endIndex = startIndex + charNum;
                            contentStream.beginText();
                            contentStream.moveTextPositionByAmount(textx, tempy);
                            contentStream.drawString(sb.substring(startIndex, endIndex > sb.length() ? sb.length() - 1 : endIndex));
                            contentStream.endText();
                            tempy -= 20;
                        }
                    } else {
                        contentStream.beginText();
                        contentStream.moveTextPositionByAmount(textx, texty);
                        contentStream.drawString(text);
                        contentStream.endText();
                    }
                    textx += colWidth;
                }
                texty -= rowHeightList.get(i);
                textx = margin + cellMargin;
            }
            contentStream.close();
            // pdf输出
            document.save(out);
            document.close();
            out.close();
            return out.toByteArray();
        } catch (Exception e) {
            log.error("图片转pdf失败：{}", e.getMessage());
            return null;
        }
    }
}
